/*
 * $Id: SkvListPath.cc,v 1.8 2006-01-30 17:38:27 bacon Exp $
 */

#include <xpp/utl/SkvListPath.h>
#include <xp/bas/assert.h>

namespace xpp
{
	namespace utl
	{

		xp_char_t* SkvListPath::getErrorStr (xp_char_t* buf, xp_size_t count) const
		{
			const xp_char_t* p;

			switch (error_code) {
			case ERR_NONE:
				p = XP_TEXT("no error");
				break;
			case ERR_SYSTEM:
				p = XP_TEXT("system error");
				break;
			case ERR_NOLABEL:
				p = XP_TEXT("section name is missing");
				break;
			case ERR_WRONGLABEL:
				p = XP_TEXT("wrong section name");
				break;
			case ERR_NOID:
				p = XP_TEXT("identifier is expected");
				break;
			default:
				p = XP_TEXT("unknown error");
				break;
			}
			
			xp_strxcpy (buf, count, p);
			return buf;
		}
		
		/////////////////////////
		// SkvListPath::read //
		/////////////////////////
		int SkvListPath::read (SkvList* skv)
		{
			error_code   = ERR_NONE;
			error_line   = 0;
			skv->clear ();
		
			xp_sio_t sio;
			if (xp_sio_open (&sio, name(), XP_TEXT("r")) == XP_NULL) {
				error_code = ERR_SYSTEM;
				return -1;	
			}

			xp_str_t line;
			if (xp_str_open (&line, 256) == XP_NULL) {
				error_code = ERR_SYSTEM;
				return -1;	
			}

			try {
				int n = read_line (&sio, &line);
				while (n != -1) {
					xp_size_t len = xp_strtrm (line.buf);
					if (len == 0) {
						n = read_line (&sio, &line);
						continue;
					}
				
					line.size = len;
					int t = parse_section_line (&sio, &line, skv);
					if (t == -1 || t == 0) break;
				}
			}
			catch (...) {
				xp_str_close (&line);
				xp_sio_close (&sio);
				throw;
			}

			xp_str_close (&line);
			xp_sio_close (&sio);

			return (error_code == ERR_NONE)? 0: -1;
		}
		
		int SkvListPath::parse_section_line (
			xp_sio_t* sio, xp_str_t* line, SkvList* skv)
		{
			// Lines beginning with # are regarded as comments
			if (is_comment(line)) return 0; 
		
			xp_char_t* label = get_label (line);
			if (label == XP_NULL) {
				error_code = ERR_NOLABEL;
				return -1;
			}
			if (label[0] == XP_CHAR('\0')) {
				error_code = ERR_WRONGLABEL;
				return -1;
			}
		
			SkvSctn* sctn = skv->findSection (label);
			if (sctn == XP_NULL) sctn = skv->addSection (label);
			xp_assert (sctn != XP_NULL);
		
			int n;
			while ((n = read_line(sio, line)) != -1) {
				// check for empty line
				xp_size_t len = xp_strtrm(line->buf);
				if (len == 0) continue;
				line->size = len;

				if (is_comment(line)) continue;
				if (is_label(line)) return 1;
				if (parse_pair_line (line, sctn) == -1) return -1;
			}

			return 0;
		}
		
		int SkvListPath::parse_pair_line (xp_str_t* line, SkvSctn* sctn)
		{
			xp_char_t* p = line->buf;
			xp_char_t* key, * val;
			xp_size_t tok_len;
		
			p = xp_strtok(p, XP_TEXT("="), &key, &tok_len);
			if (tok_len == 0) {
				error_code = ERR_NOID;
				return -1;
			}
			key[tok_len] = XP_CHAR('\0');
		
			if (p == XP_NULL) val = (xp_char_t*)XP_TEXT("");
			else {
				xp_strtok (p, XP_NULL, &val, &tok_len);
				if (tok_len == 0) val = (xp_char_t*)XP_TEXT("");
				else val[tok_len] = XP_CHAR('\0');
			}
		
			sctn->setValue (key, val);
			return 0;
		}
		
		xp_char_t* SkvListPath::get_label (xp_str_t* line)
		{
			xp_size_t len = line->size;
			xp_char_t* p = line->buf;

			if (len >= 2 && 
			    p[0] == XP_CHAR('[') && p[len - 1] == XP_CHAR(']')) {
				p[len - 1] = XP_CHAR('\0'); p++; // remove brackets
				xp_strtrm (p); 
				return p;
			}

			return XP_NULL;
		}
		int SkvListPath::is_label (const xp_str_t* line)
		{
			xp_size_t len = line->size;
			xp_char_t* p = line->buf;
			return len >= 2 && 
			       p[0] == XP_CHAR('[') &&
			       p[len - 1] == XP_CHAR(']');
		}

		int SkvListPath::is_comment (const xp_str_t* line)
		{
			xp_cint_t c = line->buf[0];
			return c == XP_CHAR('#') || c == XP_CHAR(';');
		}
		
		int SkvListPath::read_line (xp_sio_t* sio, xp_str_t* line)
		{
			if (xp_sio_getstr(sio, line) <= 0) return -1;
			error_line++;
			return 0;
		}
		
		//////////////////////////
		// SkvListPath::write //
		//////////////////////////
		int SkvListPath::write (SkvList* skv, bool append)
		{
			return write (skv, name(), append);
		}

		int SkvListPath::write (
			SkvList* skv, const xp_char_t* file, bool append)
		{
			error_code   = ERR_NONE;
			error_line   = 0;
		
			xp_sio_t sio;
			if (xp_sio_open (&sio, file, 
				((append)? XP_TEXT("a"):XP_TEXT("w+"))) == XP_NULL) {
				error_code = ERR_SYSTEM;
				return -1;	
			}

			SkvList::Node* np; 
			SkvSctn::Node* np2;
			for (np = skv->head(); np; np = np->forward()) {
				SkvSctn& s = np->value;
				xp_sio_putsn (&sio, XP_TEXT("["), s.name(), XP_TEXT("]"), XP_TEXT("\n"), XP_NULL);
		
				for (np2 = s.head(); np2; np2 = np2->forward()) {
					SkvPair& p = np2->value;
					xp_sio_putsn (&sio, p.key.buffer(), 
						XP_TEXT("="), p.value.buffer(), XP_TEXT("\n"), XP_NULL);
				}

				xp_sio_puts (&sio, XP_TEXT("\n"));
			}
			
			xp_sio_close (&sio);
			return 0;
		}
		
	}
}
